package com.dreamlike.demo.HttpUtil.Core.Generator;

import com.dreamlike.demo.HttpUtil.Annotation.Core.Request;
import com.dreamlike.demo.HttpUtil.Core.HttpDefinition;
import com.dreamlike.demo.HttpUtil.Core.ResponseBodyHandler.*;
import com.dreamlike.demo.HttpUtil.Enum.ContentType;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;

public class HttpInterfaceProxyGenerator implements InvocationHandler {
    private Map<String, HttpDefinition> httpDefinitionMap;
    private HttpRequestGenerator requestGenerator;
    private HttpClientGenerator clientGenerator;
    private HttpDefinitionGenerator httpDefinitionGenerator;

    private Map<String, ResponseBodyHandler> responseBodyHandlerMap;
    private Map<Class,Object> proxies;

    public HttpInterfaceProxyGenerator(HttpRequestGenerator requestGenerator, HttpClientGenerator clientGenerator, HttpDefinitionGenerator httpDefinitionGenerator, Map<String, ResponseBodyHandler> responseBodyHandlerMap) {
        this.requestGenerator = requestGenerator;
        this.clientGenerator = clientGenerator;
        this.httpDefinitionGenerator = httpDefinitionGenerator;
        this.responseBodyHandlerMap = responseBodyHandlerMap;


        this.responseBodyHandlerMap.put(ContentType.APPLICATION_FORM_URLENCODED, new UrlencodedResponseBodyHandler());
        this.responseBodyHandlerMap.put(ContentType.APPLICATION_JSON, new JsonResponseBodyHandler());
        this.responseBodyHandlerMap.put(ContentType.EMPTY_BODY, new EmptyResponseBodyHandler());
        this.responseBodyHandlerMap.put(ContentType.DEFAULT_STRING,new StringResponseBodyHandlerImp());
        httpDefinitionMap = new HashMap<>();
        proxies = new HashMap<>();
    }
    public HttpInterfaceProxyGenerator(HttpRequestGenerator requestGenerator, HttpClientGenerator clientGenerator, HttpDefinitionGenerator httpDefinitionGenerator, Map<String, ResponseBodyHandler> responseBodyHandlerMap, JsonResponseBodyHandler.ObjectGenerator objectGenerator) {
        this.requestGenerator = requestGenerator;
        this.clientGenerator = clientGenerator;
        this.httpDefinitionGenerator = httpDefinitionGenerator;
        this.responseBodyHandlerMap = responseBodyHandlerMap;

        this.responseBodyHandlerMap.put(ContentType.DEFAULT_STRING,new StringResponseBodyHandlerImp());
        this.responseBodyHandlerMap.put(ContentType.EMPTY_BODY, new EmptyResponseBodyHandler());
        this.responseBodyHandlerMap.put(ContentType.APPLICATION_FORM_URLENCODED, new UrlencodedResponseBodyHandler());
        this.responseBodyHandlerMap.put(ContentType.APPLICATION_JSON, new JsonResponseBodyHandler(objectGenerator));
        httpDefinitionMap = new HashMap<>();
        proxies = new HashMap<>();
    }
    public HttpInterfaceProxyGenerator(){
        this(new HttpRequestGenerator(),new HttpClientGenerator(),new HttpDefinitionGenerator(),new HashMap<>());
    }


    public void addNewResponseHandler(Map<String,ResponseBodyHandler> handlerMap){
        this.responseBodyHandlerMap.putAll(handlerMap);
    }


    public <T> T getProxy(Class<T> httpInterface){
        if (httpInterface == null || !httpInterface.isInterface()){
            return null;
        }
        Object proxy = proxies.get(httpInterface);
        if (proxy == null){
            Arrays.stream(httpInterface.getMethods())
                    .filter(m -> m.getAnnotation(Request.class) !=null)
                    .map(httpDefinitionGenerator::generate)
                    .forEach(h -> httpDefinitionMap.put(h.getOriginalMethodDesc(), h));
            proxy = Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{httpInterface}, this);
            proxies.put(httpInterface,proxy);
        }
        return (T)proxy;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String key = method.getDeclaringClass() +"."+ method.getName();
        HttpDefinition httpDefinition = httpDefinitionMap.get(key);
        if (httpDefinition == null){
            return null;
        }
        HttpRequest request = requestGenerator.generate(httpDefinition, args);
        HttpClient httpClient = clientGenerator.generateClient(httpDefinition);
        if (httpDefinition.isAsync()){
            return httpClient.sendAsync(request, getBodyHandler(httpDefinition))
                    .thenApply((response) -> responseBodyHandlerMap.getOrDefault(httpDefinition.getResponseContentType(), new StringResponseBodyHandlerImp()).trans(httpDefinition, ((HttpResponse)response).body()));
        }
        return responseBodyHandlerMap.get(httpDefinition.getResponseContentType())
                .trans(httpDefinition, httpClient.send(request, getBodyHandler(httpDefinition)).body());

    }

    //todo 目前只做了string类型
    private HttpResponse.BodyHandler getBodyHandler(HttpDefinition httpDefinition){
        return HttpResponse.BodyHandlers.ofString(Charset.defaultCharset());
    }
}
